﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ProjetoSolo.QueroGas.Dominio.Entidades;

namespace ProjetoSolo.QueroGas.Infraestrutura.PersistenciaAdo.Extensions
{
    public static class DataReaderExtensions
    {
        public static TEntidade ToEntity<TEntidade>(this IDataReader dataReader) where TEntidade : IEntidade
        {
            TEntidade entidade = default(TEntidade);

            if (!dataReader.Read()) 
                return entidade;
                     
            return GetMappedEntity<TEntidade>(dataReader);
        }

        public static IList<TEntidade> ToEntityList<TEntidade>(this IDataReader dataReader) where TEntidade : IEntidade
        {
            DataTable dt = new DataTable();
            dt.Load(dataReader);

            IList<DataRow> dr = dt.AsEnumerable().ToList();

            IList<TEntidade> entidades = new List<TEntidade>();

            while (dataReader.Read())
            {
                entidades.Add(GetMappedEntity<TEntidade>(dataReader));
            }
            
            return entidades;
        }

        private static Dictionary<String, PropertyInfo> GetNamedProperties(PropertyInfo[] propriedades)
        {            
            Dictionary<String, PropertyInfo> listaNomePropriedades = propriedades.ToDictionary(propriedade => propriedade.Name.ToUpper());

            return listaNomePropriedades;
        }

        private static object ConstructTEntidade<TEntidade>()
        {
            var flags = BindingFlags.Instance | BindingFlags.NonPublic;
            ConstructorInfo ctor = typeof(TEntidade).GetConstructor(flags, null, new Type[0], null);

            if (ctor == null)
                throw new Exception("Nenhum construtor encontrado. A entidade precisa ter um construtor privado sem parametros para que o mapeamento de DataReader funcione.");

            return ctor.Invoke(null);
        }

        private static TEntidade GetMappedEntity<TEntidade>(IDataReader dataReader) where TEntidade : IEntidade
        {
            PropertyInfo[] propriedades = typeof(TEntidade).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            Dictionary<String, PropertyInfo> listaNomePropriedades = GetNamedProperties(propriedades);

            TEntidade entidade = (TEntidade)ConstructTEntidade<TEntidade>();

            for (int i = 0; i < dataReader.FieldCount; i++)
            {
                PropertyInfo propriedade = null;

                if (listaNomePropriedades.ContainsKey(dataReader.GetName(i).ToUpper()))
                    propriedade = listaNomePropriedades[dataReader.GetName(i).ToUpper()];

                if (propriedade != null && propriedade.CanWrite)
                    propriedade.SetValue(entidade, dataReader.GetValue(i), null);
            }

            return entidade;
        }
    }
}
